#ifndef __LOGGER__
#define __LOGGER__

#include <mutex>
#include <atomic>
#include <cstdarg>
#include <sstream>
#include "Buffer.hpp"
#include "Format.hpp"
#include "LogSink.hpp"
#include "Looper.hpp"

/*

    1.日志器
        日志器用来干什么？
            对前面所有模块进行整合，提供接口完成不同等级的输出
            通过使用日志器来完成日志具体的落地

        1.同步日志器

        2.异步日志器
*/

namespace Log
{

    enum class LoggerType
    {
        LOGGER_SYNC = 0, // 同步日志器 synchronization
        LOGGER_ASYNC // 异步日志器 asynchronous
    };

    class Logger
    {
    protected:
        // 格式控制
        Formatter::ptr _fter;
        // 限制输出等级
        // 大于限制输出等级 才能输出
        LogType _limit_level; 
        // 使用锁,保证线程安全
        // 需要想清楚是哪个共享资源需要保护
        // 在一个进程内部,应该只需要存在一个日志器即可，不管是同步的还是异步的
        // 所以日志器,就是一个共享资源,什么时候需要被保护? 
        // 看哪个接口是提供给外面使用的,则那个接口需要被保护。
        // 这个接口内部可能存在很多跳转,需要自己找到正确的位置进行加锁

        // 这里的debug,warning... 是多个跳转，最后跳到Log()进行实际的落地
        // 所以我们需要在Log()内部进行加锁
        // 加锁原因在于：在实际落地中涉及到文件IO,存在写,所以肯定需要加锁保护的
        std::mutex _mt;
        // 日志器名称
        std::string _name;
        LoggerType _logger_type;
        // 落地方式数组
        std::vector<LogSink::ptr> _sinks;
    public:

        using ptr = std::shared_ptr<Logger>;

        Logger()
        {
            std::cout << "注意调用了默认构造，未初始化的" << std::endl; 
        }

        Logger( Formatter::ptr fter,
            LogType limit_level,
            string name,
            LoggerType logger_type,
            std::vector<LogSink::ptr> sinks)
            :_fter(fter),
            _limit_level(limit_level),
            _name(name),
            _logger_type(logger_type),
            _sinks(sinks.begin(),sinks.end())
        {}

        // logger->debug(__FILE__,__LINE__,"%s","测试日志")
        // %s,测试日志 对应到LogMsg中是_msg
        void debug(const char* file,const size_t line,const char* fmt,...)
        {
            va_list al; // 定义一个指针al
            va_start(al,fmt); // 将al指向可变参数部分的起始位置
            log(LogType::DEBUG,file,line,fmt,al);
            va_end(al);
        }
        void warn(const char* file,const size_t line,const char* fmt,...) 
        {
            va_list al; // 定义一个指针al
            va_start(al,fmt); // 将al指向可变参数部分的起始位置
            log(LogType::WARNING,file,line,fmt,al);
            va_end(al);
        }
        void info(const char* file,const size_t line,const char* fmt,...) 
        {
            va_list al; // 定义一个指针al
            va_start(al,fmt); // 将al指向可变参数部分的起始位置
            log(LogType::INFO,file,line,fmt,al);
            va_end(al);
        }
        void error(const char* file,const size_t line,const char* fmt,...)
        {
            va_list al; // 定义一个指针al
            va_start(al,fmt); // 将al指向可变参数部分的起始位置
            log(LogType::ERROR,file,line,fmt,al);
            va_end(al);
        }
        void fatal(const char* file,const size_t line,const char* fmt,...)
        {
            va_list al; // 定义一个指针al
            va_start(al,fmt); // 将al指向可变参数部分的起始位置
            log(LogType::FATAL,file,line,fmt,al);
            va_end(al);
        }
    protected:
        void log(LogType level,const char* file,const size_t line,const char* fmt,va_list al)
        {
            // 根据限定等级去落地
            if(_limit_level > level)
                return;

            char buf[128];
            // 1.获取可变参数
            //      a.vasprintf()        b. vnsprintf
            if(vsnprintf(buf,sizeof(buf),fmt,al) == -1)
            {
                std::cout << "Logger::log vsnprintf fail" << std::endl;
                return;
            }
            // 2.构造LogMsg
            LogMsg msg(level,file,line,_name,buf);
            // 3.日志消息格式化
            // 4.需要更具不同的落地方向进行落地，
            // 因此需要获取到格式化的消息
            //      方式1.
            // string fmt_msg = _fter->format(msg);
            // Log(fmt_msg);
            //      方式2.
            std::stringstream ss;
            _fter->format(ss,msg);
            Log(ss.str());

            // 不能这样写，因为存在同步日志，和异步日志两种方式
            // // 5.进行具体的日志落地
            // for(auto& sink : _sinks)
            //     sink->log(fmt_msg.c_str(),fmt_msg.size());

            // 使用多态，提供纯虚函数，具体落地在同步，异步下，是不同，让他们自己完成

        }
        virtual void Log(const string& fmt_msg) = 0;
    };

    // 同步日志器
    class SyncLogger:public Logger
    {
    public:
        using ptr = std::shared_ptr<SyncLogger>;
        SyncLogger()
        {}
        // 派生类初始化时，如果是父类的属性变量则需要调用父类的构造函数初始化
        SyncLogger(
                std::vector<LogSink::ptr> sinks,
                Formatter::ptr fter = Formatter::ptr(new Formatter), // 创建匿名对象,完成构造
                string name = "SyncLogger",
                LogType limit_level = LogType::DEBUG,
                LoggerType logger_type = LoggerType::LOGGER_SYNC)
            :Logger(fter,limit_level,name,logger_type,sinks)
        {}
    protected:
        // 在一个进程中,只存在一个日志器才是合理的。
        // 不同的落地方式,都涉及到了写文件，所以需要加锁进行保护
        void Log(const string& fmt_msg)
        {
            std::unique_lock<std::mutex>(_mt);
            for(auto& sinks:_sinks)
            {
                sinks->log(fmt_msg.c_str(),fmt_msg.size());
            }
        }

    };


    /*
        异步日志器：
            异步日志器有什么用？
                避免日志处时出现问题导致业务处理有问题，比如说如果落地方式是发送到网络中
                如果网络拥塞,或者对方采用阻塞式IO,会存在阻塞问题,那么会导致业务处理的阻塞
                为了避免这些因日志问题，而导致业务处理效率降低，我们采用异步方式实现具体的
                日志落地
            多线程：生产者消费者模型，采用双缓冲的设计
                1.  生产者负责将日志消息写入生产缓冲区中
                    消费者从消费缓冲区中读数据，进行具体的落地
                    消费者缓冲区中无数据后，与生产缓冲区进行交换
                2.  生产者和消费者进行数据操作(写,读) 是存在条件的
                    需要设置条件变量。生产缓冲区满了,则不能写,消费
                    缓冲区为空则不能读
                3.  多线程就需要使用锁来保证线程安全
            为什么要使用双缓冲区？而不是用队列,环形队列这样的单缓冲区？
                不使用队列的原因如下:
                    队列底层是list,使用list会导致频繁的申请空间释放空间导致效率降低。
                    解决方案：采用循环队列
                不使用循环队列的原因如下:
                    因为这是一个生产者消费者模型,是多线程。正确点说是多生产者,单消费者的模型
                    这里需要避免的冲突就比较多：生产者与生产者，消费者与生产者之间的冲突
                    锁冲突比较严重,也就是说所有线程都是串行执行,不能并发导致效率低
                    解决方案：采用双缓冲区
                双缓冲区为什么好
                    生产者将日志信息写入生产缓冲区，消费者将日志信息从消费缓冲区中读取出来
                    消费者将消费缓冲区中的数据读取完后, 消费缓冲区和生产缓冲区进行交换(指针级别的)
                    只有这里需要加锁保护，相比与循环队列来讲所冲突大大降低了。
                    生产者与生产者之间的冲突还是会存在的。
    */
    // 这里加一个设计：将生产者,消费者拎出来了,放到Looper.hpp中
    //                不放在日志器内部。
    class AsyncLogger:public Logger
    {
    private:
        AsyncLooper::ptr _looper; // 需要告诉它如何处理日志消息
    public:
        using ptr = std::shared_ptr<AsyncLogger>;
        AsyncLogger()
        {}
        // 派生类初始化时，如果是父类的属性变量则需要调用父类的构造函数初始化
        AsyncLogger(
                std::vector<LogSink::ptr> sinks,
                AsyncType type = AsyncType::ASYNC_UNSAFE, 
                Formatter::ptr fter = Formatter::ptr(new Formatter), // 创建匿名对象,完成构造
                string name = "AsyncLogger",
                LogType limit_level = LogType::DEBUG,
                LoggerType logger_type = LoggerType::LOGGER_ASYNC)
                
            :Logger(fter,limit_level,name,logger_type,sinks),
            _looper(new AsyncLooper(std::bind(&AsyncLogger::realLog,this,std::placeholders::_1)
                                    ,type))
        {
            // std::cout << LogLevel::toString(limit_level) << " 异步日志器" << name << "创建成功" << std::endl;
        }
    protected:
        // 生产者
        void Log(const string& fmt_msg)
        {
            _looper->product(fmt_msg);
        }

        //消费者 具体处理日志的方法
        void realLog(Buffer& buf)
        {
            if(_sinks.empty()) 
                return;
            for(auto& sink: _sinks)
            {
                sink->log(buf.readBegin(),buf.readable());
            }
        }

    };


    // 使用建造者模式去构建日志器
    // 为什么使用建造者模式?
    // 日志器内部存在成许多成员，大部分成员都需要进行构造
    // 如果交付给用户,用户并不清楚内部的类的成员关系
    // 所以我们直接提供一个建造者来帮助我们完成建造
    // 
    // 不需要指挥者,因为这些成员并不是按找一定的顺序
    //  
    // ！！！
    // 这里的建造者并不是仅仅建造同步日志器和异步日志器
    // 未来的日志器分为局部日志器和全局日志器
    // 这里的建造指的是建造局部日志器,还是全局日志器
    // 全局,局部日志器中又能够建在同步,异步日志器


    class LoggerBuilder
    {
    protected:
        Formatter::ptr _fter;
        LogType _limit_level; 
        std::string _name;
        LoggerType _logger_type;
        std::vector<LogSink::ptr> _sinks;
        AsyncType _async_type;
    public:
        using ptr = std::shared_ptr<LoggerBuilder>;

        LoggerBuilder()
        : _async_type(AsyncType::ASYNC_SAFE),
        _limit_level(LogType::DEBUG),
        _logger_type(LoggerType::LOGGER_ASYNC)
        {}

        void BuildLoggerType(LoggerType logger_type)
        {
            _logger_type = logger_type;
        }

        void BuildFormatter(const string& pattern)
        {
            // <> 里面是写类型!!
            // 你写ptr是一个智能指针!
            // _fter = std::make_shared<Formatter::ptr>(pattern);
            _fter = std::make_shared<Formatter>(pattern);
        }

        void BuildFormatter(const Formatter::ptr & fter)
        {
            _fter = fter;
        }
        
        void BuildLimitLevel (const LogType& limit_level)
        {
            _limit_level = limit_level;
        }
        
        void BuildLoggerName(const std::string& name)
        {
            _name = name;
        }

        void BuildEnableAsyncUnSafe()
        {
            _async_type = AsyncType::ASYNC_UNSAFE;
        }

        // 写模板是为了复用 落地类的工厂模式
        template<typename SinkType,typename ... Args>
        void BuildLoggerSinks(Args&& ... args)
        {
            auto sink = LogSinkFactory::create<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(sink);
        }
        virtual Logger::ptr Build() = 0;
    
    };

    // error：
    // 第一点是上面说的,我们建造者建造的是局部日志器和全局日志器,每个日志器中又能根据
    // 日志器的类型去建造同步日志器,建造异步日志器
    // 第二点上述基类中的Build...接口都是给外面使用的,而不是给在类内部使用!!!

    // class SyncLoggerBuilder: public LoggerBuilder
    // {
    // public:
    //     using ptr = std::shared_ptr<SyncLoggerBuilder>;
    //     Logger::ptr Build() 
    //     {
    //         BuildFormatter(Formatter::ptr()); // 传递一个匿名对象
    //         BuildLimitLevel(LogType::WARNING);
    //         BuildLoggerName("SyncLog");
    //         // 为什么老是混淆啊,这是模板需要使用<>提供类型,()完成参数传递
    //         // BuildLoggerSinks(RollBySizeLogSink);
    //         BuildLoggerSinks<RollBySizeLogSink>("./SyncLog/RBySz-");
    //         return SyncLogger::ptr(new SyncLogger(_sinks,_fter,_name,_limit_level));
    //     }
    // };
    // class AsyncLoggerBuilder: public LoggerBuilder
    // {
    // public:
    //     using ptr = std::shared_ptr<AsyncLoggerBuilder>;
    //     Logger::ptr Build()
    //     {
    //         BuildFormatter(Formatter::ptr()); // 传递一个匿名对象
    //         BuildLimitLevel(LogType::WARNING);
    //         BuildLoggerName("AsyncLog");
    //         // 为什么老是混淆啊,这是模板需要使用<>提供类型,()完成参数传递
    //         // BuildLoggerSinks(RollBySizeLogSink);
    //         BuildLoggerSinks<RollBySizeLogSink>("./AsyncLog/RBySz-");
    //         return std::make_shared<AsyncLogger>(_sinks,_async_type,_fter,_name,_limit_level);
    //     }
    // };
    
    class LocalLoggerBuilder: public LoggerBuilder
    {
    public:
        using ptr = std::shared_ptr<LocalLoggerBuilder>;
        Logger::ptr Build()
        {
            if(_sinks.empty())
            {
                std::cout << "LocalLoggerBuilder::_sinks is empty" << std::endl;
                abort();
            }
            if(_fter.get() == nullptr)
            {
                std::cout << "LocalLoggerBuilder::_fter is nullptr" << std::endl;
                abort();
            }
            if(_name.empty())
            {
                std::cout << "LocalLoggerBuilder::_name is empty" << std::endl;
                abort();
            }

            Logger::ptr logger;
            if(_logger_type == LoggerType::LOGGER_ASYNC)
            {
                logger = std::make_shared<AsyncLogger>(_sinks,_async_type,
                _fter,_name,_limit_level); 
            }
            else
            {
                logger = std::shared_ptr<SyncLogger>(new SyncLogger\
                                (_sinks,_fter,_name,_limit_level)); // 匿名对象构造
            }
            return logger;
        }
    };



    // 全局的日志,使用单例模式
    // 使用简易版本的,c++11后才能使用
    // c++11规定 static是线程安全的
    // 不会写在派生类中写单例
    // 全局日志器 不能写在这里
    // 会导致头文件循环包含的问题
    // 只能写到LogManager.hpp中
    // class GlobalLoggerBuilder:public LoggerBuilder


}

#endif
